#include"Common.cpp"
#include"ThreadCache.hpp"
#include"CentralCache.hpp"
#include"PageCache.hpp"
using namespace std;
struct TreeNode
{
	int _val;
	TreeNode* _left;
	TreeNode* _right;
	TreeNode()
		:_val(0)
		, _left(nullptr)
		, _right(nullptr)
	{}
};

void TestObjectPool()
{
	// 申请释放的轮次
	const size_t Rounds = 3;
	// 每轮申请释放多少次
	const size_t N = 1000000;
	std::vector<TreeNode*> v1;
	v1.reserve(N);

	//malloc和free
	size_t begin1 = clock();
	for (size_t j = 0; j < Rounds; ++j)
	{
		for (size_t i = 0; i < N; ++i)
		{
			v1.push_back(new TreeNode);
		}
		for (size_t i = 0; i < N; ++i)
		{
			delete v1[i];
		}
		v1.clear();
	}
	size_t end1 = clock();

	//定长内存池
	ObjectPool<TreeNode> TNPool;
	std::vector<TreeNode*> v2;
	v2.reserve(N);
	size_t begin2 = clock();
	for (size_t j = 0; j < Rounds; ++j)
	{
		for (size_t i = 0; i < N; ++i)
		{
			v2.push_back(TNPool.New());
		}
		for (size_t i = 0; i < N; ++i)
		{
			TNPool.Delete(v2[i]);
		}
		v2.clear();
	}
	size_t end2 = clock();

	cout << "new cost time:" << end1 - begin1 << endl;
	cout << "object pool cost time:" << end2 - begin2 << endl;
}

static void* ConcurrentAlloc(size_t size)
{
  if(size>MAX_BYTES)
  {
    size_t alignSize=SizeClass::RoundUp(size);
    size_t kPage=alignSize>>PAGE_SHIFT;

    PageCache::GetInstance()->_pageMtx.lock();
    Span* span=PageCache::GetInstance()->NewSpan(kPage);
    //span->_isUse.store(true);
    span->_isUse = true;
    span->_objSize=size;
    PageCache::GetInstance()->_pageMtx.unlock();

    void* ptr=(void*)(span->_pageId<<PAGE_SHIFT);
    return ptr;
  }
  else
  {
    if(pTLSThreadCache==nullptr)
    {
      //pTLSThreadCache = new ThreadCache;
      static std::mutex tcMtx;
      static ObjectPool<ThreadCache> tcPool;
      tcMtx.lock();
      pTLSThreadCache = tcPool.New();
      tcMtx.unlock();
    }
  return pTLSThreadCache->Allocate(size);
  }
}
static void ConcurrentFree(void* ptr)
{
  Span* span=PageCache::GetInstance()->MapObjectToSpan(ptr);
  size_t size=span->_objSize;
  if(size>MAX_BYTES)
  {
    PageCache::GetInstance()->_pageMtx.lock();
    PageCache::GetInstance()->ReleaseSpanToPageCache(span);
    PageCache::GetInstance()->_pageMtx.unlock();
  }
  else
  {
    	assert(pTLSThreadCache);
	    pTLSThreadCache->Deallocate(ptr,size);
  }
}
void BenchmarkMalloc(size_t ntimes,size_t nworks,size_t rounds)
{
  std::vector<std::thread> vthread(nworks);
  std::atomic<size_t> malloc_costtime(0);
  std::atomic<size_t> free_costtime(0);
  for(size_t k=0;k<nworks;++k)
  {
    vthread[k]=std::thread([&,k](){
      std::vector<void*> v;
      v.reserve(ntimes);
      for(size_t j=0;j<rounds;++j)
      {
        size_t begin1=clock();
        for(size_t i=0;i<ntimes;i++)
        {
          //v.push_back(malloc(16));
          v.push_back(malloc((16+i)%8192+1));
        }
        size_t end1=clock();
        size_t begin2=clock();
        for(size_t i=0;i<ntimes;i++)
        {
          free(v[i]);
        }
        size_t end2=clock();
        v.clear();
        malloc_costtime+=end1-begin1;
        free_costtime+=end2-begin2;
      }
    });
  }
  for(auto& t:vthread)
  {
    t.join();
  }
  cout<<nworks<<"threads,"<<rounds<<"rounds,"<<ntimes<<"times"<<"  malloc cost time:"<<malloc_costtime.load()<<endl;
  cout<<nworks<<"threads,"<<rounds<<"rounds,"<<ntimes<<"times"<<"  free cost time:"<<free_costtime.load()<<endl;
  cout<<nworks<<"threads,"<<rounds<<"rounds,"<<ntimes<<"times"<<"  total cost time:"<<malloc_costtime.load()+free_costtime.load()<<endl;
}

void BenchmarkConcurrentAlloc(size_t ntimes,size_t nworks,size_t rounds)
{
  std::vector<std::thread> vthread(nworks);

  std::atomic<size_t> malloc_costtime(0);
  std::atomic<size_t> free_costtime(0);
 
  for(size_t k=0;k<nworks;++k)
  {
    vthread[k]=std::thread([&](){
      std::vector<void*> v;
      v.reserve(ntimes);
      for(size_t j=0;j<rounds;++j)
      {
        size_t begin1=clock();
        for(size_t i=0;i<ntimes;i++)
        {
          //v.push_back(ConcurrentAlloc(16));
          v.push_back(ConcurrentAlloc((16+i)%8192+1));
        }
        size_t end1=clock();
        size_t begin2=clock();
        for(size_t i=0;i<ntimes;i++)
        {
          ConcurrentFree(v[i]);
        }
        size_t end2=clock();
        v.clear();
        malloc_costtime+=(end1-begin1);
        free_costtime+=(end2-begin2);
      }
    });
  }
  for(auto& t:vthread)
  {
    t.join();
  }
  cout<<nworks<<"threads,"<<rounds<<"rounds,"<<ntimes<<"times"<<"  concurrent alloc cost time:"<<malloc_costtime.load()<<endl;
  cout<<nworks<<"threads,"<<rounds<<"rounds,"<<ntimes<<"times"<<"  concurrent free cost time:"<<free_costtime.load()<<endl;
  cout<<nworks<<"threads,"<<rounds<<"rounds,"<<ntimes<<"times"<<"  total cost time:"<<malloc_costtime.load()+free_costtime.load()<<endl;
}
int main()
{
  //TestObjectPool();
  // for(size_t i=0;i<1024;i++)
  // {
  //   void *p1=ConcurrentAlloc(6);
  // }
  

   /*void *p1=ConcurrentAlloc(6);
   void *p2=ConcurrentAlloc(8);
   void *p3=ConcurrentAlloc(1);

   ConcurrentFree(p1);
   ConcurrentFree(p2);
   ConcurrentFree(p3);
   cout<<"completel"<<endl;*/
  //void* p1=ConcurrentAlloc(257*1024);
  // ConcurrentFree(p1);

  // void* p2=ConcurrentAlloc(129*8*1024);
  // ConcurrentFree(p2);
  // return 0;
  size_t n=10000;
  cout<<"======================================================="<<endl;
  BenchmarkConcurrentAlloc(n,4,10);

  cout<<endl<<endl;
  cout<<"======================================================="<<endl;
  BenchmarkMalloc(n,4,10);
  
  return 0;
}
